#ifndef HANDLERDB_H
#define HANDLERDB_H

#include <typeinfo>
#include <vector>
#include <map>
#include <string>
#include <list>
#include <assert.h>
#include <boost/functional/hash.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/function.hpp>
#include <boost/preprocessor/comma_if.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#include "jncInterface.h"
#include "jncDefs.h"
#include "jncFnWrapper.h"
#include <assert.h>
#include <bits/stream_iterator.h>


#define USE          "__use"
#define ID           "__id"
#define USEID        "__use__id"
#define PTRTOKEN     "p_"
#define ARRTOKEN     "a_"
#define PARRTOKEN    "a_p_"

// Build Datablase tables for registered objects
// TODO: This will change in near future to use
// a persistent database (ColdStore)

#define CREATEHANDLE(table, fn_p)				\
  __entry<T> tmp[] = { prependName(oname.c_str(), fn_p) };	\
  int size = sizeof(tmp)/sizeof(__entry<T>);			\
  table.resize(size, fn_p);					\
  std::copy(tmp, (tmp + size), table.begin());
#define CREATECONS(table, fn_p)					\
  __entry<T> tmp[] = {						\
    prependName(oname.c_str(), fn_p),				\
    makePtr(fn_p),						\
    makeArray(fn_p),						\
    makeArrayIndex(fn_p),					\
    makeArrayElement(fn_p),					\
    makePtrArray(fn_p),						\
    makePtrArrayIndex(fn_p),					\
    makePtrArrayElement(fn_p)					\
  };								\
  int size = sizeof(tmp)/sizeof(__entry<T>);			\
  table.resize(size, fn_p);						\
  std::copy(tmp, (tmp + size), table.begin());

/* #define CREATENEWENTRY(at, name, db, fn_p, rname)		\ */
/*   if(!name || name != fn_p.pssignature->begin()->c_str())	\ */
/*     assert(name = fn_p.pssignature->begin()->c_str());			\ */
/*   db.getTable()->insert(at, std::pair<Idnb, Idnb>(computeId(rname.c_str()), computeId(name))) // use primary tag as a lookup key */

#define MODFNPATTERN(var, fname, arg)		\
  var = new std::list<std::string>(2);		\
  *var->begin() = fname;			\
  *(++var->begin()) = arg
#define MODTYPATTERN(var, fntype, arg)	\
  var = new std::list<jncTypei>(2);	\
  *var->begin() = fntype;		\
  *(++var->begin()) = arg
#define MODCAPATTERN(var, fcall, lcall)				\
  var = new std::pair<gnCallback, gnCallback>(fcall, lcall)

struct spointer {
  spointer():ptr(NULL){}
  void* ptr;
};
  

Idnb computeId(const char* s);
Idnb computeId(const std::vector<std::string>& tok);

template<class T>
struct __entry {
  __entry(T* o, 
	  std::list<std::string>* ssignature,
	  std::list<jncTypei>* tsignature,
	  std::pair<void*, void*>* clbs,
	  Priority lvl)
    :obj(o), pssignature(ssignature), ptsignature(tsignature), callbacks(clbs), level(lvl) 
  {
    assert(o);
    for(std::list<std::string>::iterator i = ssignature->begin(); i != ssignature->end(); i++)
      assert(i->c_str());
  }
  T* obj;
  std::list<std::string>* pssignature;
  std::list<jncTypei>* ptsignature;
  std::pair<gnCallback, gnCallback>* callbacks;
  Priority level;
};


class HandlerTable {
 public:
  struct HandlerInfo {
    typedef __entry<JncInterface>      CallBackInfo;
    std::pair<Idnb, Idnb> id;
    std::map<Idnb, CallBackInfo*> memberfnts;
    HandlerInfo(std::pair<Idnb, Idnb> i): id(i) {}
  };
  HandlerTable() : Hdlers(NULL)
    { Hdlers = getStorage(); }
    std::list<HandlerTable::HandlerInfo>* getTable()
      { return Hdlers;}
    void setTable(std::list<HandlerTable::HandlerInfo>* p)
    { Hdlers = p;}
 protected:
    std::list<HandlerTable::HandlerInfo>* getStorage()
      { return new std::list<HandlerTable::HandlerInfo>();}
 private:
    std::list<HandlerTable::HandlerInfo>* Hdlers;
};

template<class T>
__entry<T>
makeUseWrapper(__entry<T> e) {
  __entry<T> tmp(e);
  tmp.pssignature = new std::list<std::string>(e.pssignature->begin(), e.pssignature->end());
  tmp.pssignature->insert(++tmp.pssignature->begin(), USE);
  tmp.ptsignature = new std::list<jncTypei>(*e.ptsignature);
  tmp.ptsignature->insert(++tmp.ptsignature->begin(), JCMP);
  tmp.obj = new UseWrapper<T>(*tmp.obj);
  return tmp;
}

template<class T>
__entry<T>
makeIdWrapper(__entry<T> e) {
  __entry<T> tmp(e);
  tmp.pssignature = new std::list<std::string>(e.pssignature->begin(), e.pssignature->end());
  tmp.pssignature->insert(++tmp.pssignature->begin(), ID);
  tmp.ptsignature = new std::list<jncTypei>(*e.ptsignature);
  tmp.ptsignature->insert(++tmp.ptsignature->begin(), JCMP);
  tmp.obj = new IdWrapper<T>(*tmp.obj);
  return tmp;
}

template<class T>
__entry<T>
makeUseIdWrapper(__entry<T> e) {
__entry<T> tmp(e);
  tmp.pssignature = new std::list<std::string>(e.pssignature->begin(), e.pssignature->end());
  tmp.pssignature->insert(++tmp.pssignature->begin(), ID);
  tmp.pssignature->insert(++tmp.pssignature->begin(), USE);
  tmp.ptsignature = new std::list<jncTypei>(*e.ptsignature);
  tmp.ptsignature->insert(++tmp.ptsignature->begin(), JCMP);
  tmp.ptsignature->insert(++tmp.ptsignature->begin(), JCMP);
  tmp.obj = new UseIdWrapper<T>(*tmp.obj);
  return tmp;
}

template<class T>
__entry<T> makePtr(__entry<T> e) {
  __entry<T> tmp(e);
  std::string name(*e.pssignature->begin());
  assert(name.find(CLASSDELM) >= 0);
  std::string s(name.begin(), (name.begin() + name.find(CLASSDELM)));
  std::string fn(s + CLASSDELM + PTRTOKEN + s);
  MODFNPATTERN(tmp.pssignature, fn, s);
  MODTYPATTERN(tmp.ptsignature, P_JCMP, JCMP);
  gnCallback f(new boost::function2<gReturnCode, T*, jncTypev>(&T::makeCopy));
  MODCAPATTERN(tmp.callbacks, tmp.callbacks->first, f);
  return tmp;
}

template<class T>
__entry<T> makeArray(__entry<T> e) {
  __entry<T> tmp(e);
  std::string name(*e.pssignature->begin());
  assert(name.find(CLASSDELM) >= 0);
  std::string s(name.begin(), (name.begin() + name.find(CLASSDELM)));
  std::string fn(s + CLASSDELM + ARRTOKEN + s);
  MODFNPATTERN(tmp.pssignature, fn, "length");
  MODTYPATTERN(tmp.ptsignature, A_JCMP, JCMP);
  gnCallback f(new boost::function2<gReturnCode, T*, jncTypev>(&T::makeArray));
  MODCAPATTERN(tmp.callbacks, tmp.callbacks->first, f);
  return tmp;
}

template<class T>
__entry<T> makePtrArray(__entry<T> e) {
  __entry<T> tmp(e);
  std::string name(*e.pssignature->begin());
  assert(name.find(CLASSDELM) >= 0);
  std::string s(name.begin(), (name.begin() + name.find(CLASSDELM)));
  std::string fn(s + CLASSDELM + PARRTOKEN + s);
  MODFNPATTERN(tmp.pssignature, fn, "length");
  MODTYPATTERN(tmp.ptsignature, A_P_JCMP, P_JCMP);
  gnCallback f(new boost::function2<gReturnCode, T*, jncTypev>(&T::makePtrArray));
  MODCAPATTERN(tmp.callbacks, tmp.callbacks->first, f);
  return tmp;
}

template<class T>
__entry<T> makeArrayIndex(__entry<T> e) {
  __entry<T> tmp(e);
  std::string name(*e.pssignature->begin());
  assert(name.find(CLASSDELM) >= 0);
  std::string s(name.begin(), (name.begin() + name.find(CLASSDELM)));
  std::string fn(s + CLASSDELM + ARRTOKEN + s);
  MODFNPATTERN(tmp.pssignature, fn, (PTRTOKEN + s));
  tmp.pssignature->insert(tmp.pssignature->end(), "index");
  MODTYPATTERN(tmp.ptsignature, JCMP, P_JCMP);
  tmp.ptsignature->insert(tmp.ptsignature->end(), JCMP);
  gnCallback f(new boost::function4<gReturnCode, T*, jncTypev, jncTypev, jncTypev>(&T::indexArray));
  MODCAPATTERN(tmp.callbacks, tmp.callbacks->first, f);
  return tmp;
}

template<class T>
__entry<T> makePtrArrayIndex(__entry<T> e) {
  __entry<T> tmp(e);
  std::string name(*e.pssignature->begin());
  assert(name.find(CLASSDELM) >= 0);
  std::string s(name.begin(), (name.begin() + name.find(CLASSDELM)));
  std::string fn(s + CLASSDELM + PARRTOKEN + s);
  MODFNPATTERN(tmp.pssignature, fn, (PTRTOKEN + (PTRTOKEN + s)));
  tmp.pssignature->insert(tmp.pssignature->end(), "index");
  MODTYPATTERN(tmp.ptsignature, P_JCMP, A_P_JCMP);
  tmp.ptsignature->insert(tmp.ptsignature->end(), JCMP);
  gnCallback f(new boost::function4<gReturnCode, T*, jncTypev, jncTypev, jncTypev>(&T::indexPtrArray));
  MODCAPATTERN(tmp.callbacks, tmp.callbacks->first, f);
  return tmp;
}

template<class T>
__entry<T> makeArrayElement(__entry<T> e) {
  __entry<T> tmp(e);
  std::string name(*e.pssignature->begin());
  assert(name.find(CLASSDELM) >= 0);
  std::string s(name.begin(), (name.begin() + name.find(CLASSDELM)));
  std::string fn(s + CLASSDELM + ARRTOKEN + s);
  MODFNPATTERN(tmp.pssignature, fn, (PTRTOKEN + s));
  tmp.pssignature->insert(tmp.pssignature->end(), "index");
  tmp.pssignature->insert(tmp.pssignature->end(), s);
  MODTYPATTERN(tmp.ptsignature, A_JCMP, P_JCMP);
  tmp.ptsignature->insert(tmp.ptsignature->end(), JCMP);
  tmp.ptsignature->insert(tmp.ptsignature->end(), JCMP);
  gnCallback f(new boost::function4<gReturnCode, T*, jncTypev, jncTypev, jncTypev>(&T::indexArray));
  MODCAPATTERN(tmp.callbacks, tmp.callbacks->first, f);
  return tmp;
}

template<class T>
__entry<T> makePtrArrayElement(__entry<T> e) {
  __entry<T> tmp(e);
  std::string name(*e.pssignature->begin());
  assert(name.find(CLASSDELM) >= 0);
  std::string s(name.begin(), (name.begin() + name.find(CLASSDELM)));
  std::string fn(s + CLASSDELM + PARRTOKEN + s);
  MODFNPATTERN(tmp.pssignature, fn, (PTRTOKEN + (PTRTOKEN + s)));
  tmp.pssignature->insert(tmp.pssignature->end(), "index");
  tmp.pssignature->insert(tmp.pssignature->end(), (PTRTOKEN + s));
  MODTYPATTERN(tmp.ptsignature, A_JCMP, P_JCMP);
  tmp.ptsignature->insert(tmp.ptsignature->end(), JCMP);
  tmp.ptsignature->insert(tmp.ptsignature->end(), P_JCMP);
  gnCallback f(new boost::function4<gReturnCode, T*, jncTypev, jncTypev, jncTypev>(&T::indexPtrArray));
  MODCAPATTERN(tmp.callbacks, tmp.callbacks->first, f);
  return tmp;
}

template<class T>
void store(HandlerTable::HandlerInfo& ind, __entry<T> e) {
std::string s;
 std::for_each(e.pssignature->begin(), e.pssignature->end(), s += boost::lambda::_1);
 ind.memberfnts[computeId(s.c_str())] = new HandlerTable::HandlerInfo::CallBackInfo(e.obj, e.pssignature, e.ptsignature, e.callbacks, e.level);
}

template<class T>
void storeFnProprieties(HandlerTable::HandlerInfo& ind, __entry<T> e)
{
  store(ind, e);
  store(ind, makeUseWrapper(e));
  store(ind, makeIdWrapper(e));
  store(ind, makeUseIdWrapper(e));
}

template<class T>
__entry<T> prependName(const char* name, __entry<T> e) {
  std::string s(std::string(name) + CLASSDELM + *e.pssignature->begin());
  *e.pssignature->begin() = s;
  return e;
}

template<class T>
void operator << (HandlerTable& hdb, __entry<T> e) {
  std::list<HandlerTable::HandlerInfo>::iterator p;
  std::vector<__entry<T> > tbl;
  static std::string oname;
  std::string cname(typeid(*e.obj).name());
  for(p = hdb.getTable()->begin();; p++)
    if(p != hdb.getTable()->end())
      {
	if( // Search object using runtime name or primary tag name
	   (computeId(cname.c_str()) == p->id.first) || 
	   (computeId(e.pssignature->begin()->c_str()) == p->id.second))
	  { // Class Table found
	    CREATEHANDLE(tbl, e); // create required records for mfn
	    break; 
	  }
      }
    else
      { // Create new class Table
	if(!oname.length() || oname != *e.pssignature->begin())
	  oname = *e.pssignature->begin();
	hdb.getTable()->insert(p, std::pair<Idnb, Idnb>(computeId(cname.c_str()), computeId(oname.c_str()))); // use primary tag as a lookup key
	CREATECONS(tbl, e); // create required records for object
	--p;
	break;
      }
  for(int i = 0; i < tbl.size(); i++)
    storeFnProprieties(*p, tbl[i]);
}

HandlerTable* AllocNamespaceTable();

typedef HandlerTable::HandlerInfo::CallBackInfo* FnInfo;

#endif
